﻿package de.gameduell.framework.facebook.app.pyramide {
	import de.gameduell.framework.debug.Debug;
	import de.polygonal.ds.Array2;

	import com.adobe.serialization.json.JSON;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.geom.Point;

	public class Pyramid extends EventDispatcher {

		public static const SETUP_DONE:String = "PYRAMID_SETUP_DONE";
		public static const NEW_CONSTRUCTION:String = "NEW_CONSTRUCTION";
		public static const OUT_OF_BRICKS:String = "OUT_OF_BRICKS";
	
		private var _bricks:Array;
		private var currentBrickId:String;
		private var _pyramid:Array2;
		private var pyramidBricks:Array2;
		private var currentX:int = 0;
		private var currentY:int = 0;
		private var _brickCatalog:Array;
		private var _brickConditions:Array;
		private var _newBricks:Array;
		private var _isForeign:Boolean;

		public function Pyramid(aForeignPyramid:Boolean = false) {
			_isForeign = aForeignPyramid;
			_brickConditions = new Array();
			_brickCatalog = new Array("brick_1","brick_2"); // default setup
			_newBricks = new Array();
			_bricks = new Array();
			_pyramid = new Array2(1, 1);
			pyramidBricks = new Array2(8, 8); // TODO - check if it's enough!
		}
		
		public function get isForeign():Boolean {
			return _isForeign;
		}
		
		public function get currentBrick():Brick {
			return _bricks[currentBrickId];
		}

		public function get brickConditions():Array {
			return _brickConditions;
		}
		
		public function getBrickConditionbyType(aType:String):BrickCondition {
			return BrickCondition(_brickConditions[aType]);
		}
		
		public function get brickCatalog():Array {
			return _brickCatalog;
		}
		
		public function get newBricks():Array {
			Debug.trace("Pyramid::newBricks: currentBrickId = " + currentBrickId, Debug.DEBUG);
			var myBrick:Brick = getBrickByID(String(int(currentBrickId)));
			return myBrick.brickTypesAvailable;
		}
		
		public function nextBrick():void {
			currentBrickId = String(int(currentBrickId) + 1);
			try{
				getBrickByID(currentBrickId).state = Brick.STATE_CONSTRUCTION;
				dispatchEvent(new Event(NEW_CONSTRUCTION));
				
				if(int(currentBrickId) > 1){
					var previousBrick:Brick = getBrickByID(String(int(currentBrickId) - 1));
					if(previousBrick.state == Brick.STATE_COMPLETED){
						previousBrick.addEventListener(Brick.BRICK_TYPE_CHANGED, onBrickTypeChanged);
						for(var n:int = 0; n < previousBrick.brickTypesAvailable.length ; n++){
							_brickCatalog.push(previousBrick.brickTypesAvailable[n]);
						}
					}
				}
			}catch(e:Error){
				dispatchEvent(new Event(OUT_OF_BRICKS));
			}
		}

		public function getBricksByRow(aRowId:int):Array{
			return pyramidBricks.getCol(aRowId);
		}

		public function setup(aMissionListXML:XMLList, aBrickConditionList:XMLList):void {
			for(var j:* in aMissionListXML){
				var aBrick:XML = XML(aMissionListXML[j]);
				addBrick(aBrick);
			}
			for(var i:* in aBrickConditionList){
				var aBrickCondition:XML = XML(aBrickConditionList[i]);
				var aCondition:BrickCondition = new BrickCondition(aBrickCondition);
				_brickConditions[aCondition.brickType] = aCondition;
			}
		}
		
		public function pyramidToString():String {
			var pyramidArray:Array = new Array();
			for each(var myBrick:Brick in bricks){
				if(myBrick.state != Brick.STATE_UNDEFINED){
					var brickObject:Object = new Object();
					brickObject.id = myBrick.id;
					brickObject.state = myBrick.state;
					brickObject.brickType = myBrick.brickType;
					brickObject.score = myBrick.score;
					pyramidArray.push(JSON.encode(brickObject));
				}
			}
			if(pyramidArray.length == 0){
				Debug.trace("Pyramid::pyramidToString: NO USER DATA AVAILABLE - restoring defaults ...", Debug.WARNING);
				var defaultObject:Object = new Object();
				defaultObject.id = "1";
				defaultObject.state = Brick.STATE_CONSTRUCTION;
				defaultObject.brickType = "brick_const_1";
				defaultObject.score = "0";
				pyramidArray.push(JSON.encode(defaultObject));
			}
			return pyramidArray.join(";");
		}

		public function get size():int {
			return _pyramid.height;
		}

		private function addBrick(aBrickXML:XML):void {
			var aBrick:Brick = new Brick(aBrickXML);
			bricks[aBrick.id] = aBrick;
			
			var newPos:Point = nextBrickPosition();
			_pyramid.set(newPos.x, newPos.y, 1); // save model
			pyramidBricks.set(newPos.x, newPos.y, aBrick); // save bricks
			aBrick.position = newPos;
			aBrick.state = Brick.STATE_UNDEFINED;
		}
		
		private function onBrickTypeChanged(event:Event):void {
			dispatchEvent(new Event(NEW_CONSTRUCTION)); // TODO new event?
		}
		
		public function stringToPyramid(serverDataString:String, cleanData:Boolean = false):void {
			var aBrickId:String = "1";
			var restoredBrickList:Array = serverDataString.split(";");
			Debug.trace("Pyramid::stringToPyramid: found: " + restoredBrickList.length + " bricks.", Debug.DEBUG);
			if(cleanData){
				Debug.trace("Pyramid::stringToPyramid: cleaning old pyramid data ...", Debug.DEBUG);
				for each(var brickToClean:Brick in _bricks){
					brickToClean.state = Brick.STATE_UNDEFINED;
				}
			}
			for(var i:int = 0; i < restoredBrickList.length; i++){
				var restoredBrickObject:Object = JSON.decode(restoredBrickList[i]);
				var brickToUpdate:Brick = getBrickByID(restoredBrickObject["id"]);
				brickToUpdate.state = restoredBrickObject["state"];
				brickToUpdate.brickType = restoredBrickObject["brickType"];
				brickToUpdate.score = restoredBrickObject["score"];
				aBrickId = restoredBrickObject["id"];

				// enable pyramid for re-skinning
				if(brickToUpdate.state == Brick.STATE_COMPLETED){
					brickToUpdate.addEventListener(Brick.BRICK_TYPE_CHANGED, onBrickTypeChanged);
					for(var n:int = 0; n < brickToUpdate.brickTypesAvailable.length ; n++){
						_brickCatalog.push(brickToUpdate.brickTypesAvailable[n]);
					}
				}
			}
			currentBrickId = aBrickId;
		}
		
		public function get bricks():Array {
			return _bricks;
		}
		
		public function getBrickByID(aID:String):Brick {
			return Brick(_bricks[aID]);
		}
		
		private function nextBrickPosition():Point {
			if(getColSum(currentX) < _pyramid.width) {
				if(getColSum(currentX) == 1) {
					currentX = 0;
					_pyramid.appendRow(createNullArray(_pyramid.width));
					currentY = getColSum(currentX);
				} else {
					if(currentX < _pyramid.width - 1) {
						currentX++;
						currentY = getColSum(currentX);
					}else if(getColSum(currentX) == 2) {
						_pyramid.appendCol(createNullArray(_pyramid.height));
						currentX++;
						currentY = 0;
					}
				}
			}
			else if(getColSum(currentX) == _pyramid.height) {
				if(getColSum(currentX) == 2) {
					_pyramid.appendCol(createNullArray(_pyramid.height));
					currentX = _pyramid.width - 1;
					currentY = 0;
				} else {
					if(_pyramid.width == 1){
						currentX = 0;
					}else{
						currentX = _pyramid.width - (_pyramid.width - 1);
					}
					currentY = getColSum(currentX);
					if(getColSum(currentX) == _pyramid.height) {
						_pyramid.appendRow(createNullArray(_pyramid.width));
					}
				}
			} else {
				currentX++;
				if(currentX > _pyramid.width - 1) {
					_pyramid.appendCol(createNullArray(_pyramid.height));
				}
				currentY = getColSum(currentX);
			}
			return new Point(currentX, currentY);
		}

		private function createNullArray(aLength:int):Array {
			var resultArray:Array = new Array();
			for(var i:int = 0;i < aLength;i++) {
				resultArray.push(0);
			}
			return resultArray;
		}

		private function getColSum(colIndex:int):int {
			var solSum:int = 0;
			var col:Array = _pyramid.getCol(colIndex);
			for(var i:int = 0;i < col.length;i++) {
				solSum += col[i];
			}
			return solSum;
		}
		
		public function get totalScore():int {
			var totalScore:int = 0;
			for(var i:* in bricks) {
				totalScore += Brick(bricks[i]).score;
			}
			return totalScore;
		}
	}
}
